// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Jeff Stewart
//
// Adapts a RenamableSslSocket for line-based reading with timeouts.

#ifndef POPDIAG_PROBES_LINEREADER_H__
#define POPDIAG_PROBES_LINEREADER_H__


#include <QtCore/QString>
#include <QtCore/QTimer>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QtNetwork>

namespace popdiag {

typedef enum {
  DATA_READ,
  TIMEOUT,
  SOCKET_DROPPED
} LineReadResult;

// TODO: Find a better home for this:
// if only setPeerName() were public.  Alas.
// We need the "hostname" as passed to the
// socket constructor to match the certificate,
// or QtSslSocket gets upset.  But we want to
// connect to a specific IP.  So:
class RenamableSslSocket : public QSslSocket {
  Q_OBJECT
 public:
  RenamableSslSocket() : QSslSocket(NULL) {
  }
  inline void SetDnsName(const QString &name) {
    setPeerName(name);
  }

  // The following are for testing, and normally just call the
  // respective method in QSslSocket.
  void connectToHost(const QHostAddress &address, quint16 port);
  qint64 write(const QByteArray &byteArray);
  bool flush();
 public Q_SLOTS:
  void startClientEncryption();
};

// Reads lines off a RenamableSslSocket with a timeout per line
// Assumes it is reading in UTF-8.
class LineReader : public QObject {
  Q_OBJECT
 public:
  // Create a reader to read from the given socket.
  // If a line is not read in timeoutInMillis, a timeout event will be
  // generated.  Use Cleanup() to cancel future timeouts.
  LineReader(RenamableSslSocket *src, int timeoutInMillis);
  ~LineReader();

  // Read a line of data if one is available without blocking.
  // If this returns false, DoneReading() will be emitted at a future time.
  bool NonblockingRead(QString *out);

  // Reads any already-queued up data, but doesn't attempt
  // to read any more.
  bool ReadFromAccum(QString *out);

  // Disconnect from the source.  Stop the timeout.
  void Cleanup();

 signals:
  // Emitted when this line reader finishes reading some
  // content.  May emit DataRead spuriously on SSL sockets.
  void DataReady(LineReadResult r);

 private slots:
  void OnDataReady();
  void TryRead(bool send_signals);
  void OnTimeout();

 private:
  RenamableSslSocket *src_;
  QString accum_;
  const int timeout_;
  QTimer *timer_;
};


}  // namespace popdiag

#endif  // POPDIAG_PROBES_LINEREADER_H__
